// Dictionary.h
//
// Copyright (c) 2008 Bojan Rajkovic
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//

#ifndef _DICTIONARY_H_
#define _DICTIONARY_H_ 1

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
#include <algorithm>
#include <map>
#include <stdint.h>
#include <ctype.h>
#include <boost/foreach.hpp>
#include <boost/algorithm/string.hpp>

// Useful macros/constants
#define foreach BOOST_FOREACH
// Since g++ apparently refuses to see this #define-ed in stdint.h
#ifndef UINT64_MAX
	#define UINT64_MAX             18446744073709551615ULL
#endif

namespace UltraHAL {

	class Dictionary {
	
		class DictionaryException {
		
			private:
			
				std::string errorMessage;
		
			public:
			
				DictionaryException(std::string Message) { errorMessage = Message; }			
				virtual const char* what() throw () { return errorMessage.c_str(); }
		};
	
		private:
		
			// All the words in the dictionary.
			std::vector<std::string> words;
			// Gets the soundex key of a word.
			std::string SoundexKey(std::string);
			// Two maps, one that maps a word to a list of sound-alike words, and another that maps a list of characters
			// to a list of words that only use those characters
			std::map<std::string, std::vector<std::string> > soundexMap, characterMap;
			// A map that holds the values for each soundex character mapping
			std::map<char, uint8_t> soundexCharacterMap;
			// A map that maps words to a minimum length/maximum length combination. Using uint64_t allows for some
			// ungodly huge words.
			std::map<std::pair<std::pair<uint64_t,uint64_t>, bool>, std::vector<std::string> > lengthMap;
			// A map that caches the positions of words--don't forget to clear this during a Sort().
			std::map<std::string, uint64_t> positionMap;
			// A map that caches the soundex key of a word
			std::map<std::string, std::string> soundexKeysMap;
			// A map that caches which words have which first letter
			std::map<char, std::vector<std::string> > firstCharacterMap;
				 
		public:
					
			// Constructor and destructor.
			explicit Dictionary(void);
			Dictionary(std::vector<std::string>);
			explicit Dictionary(std::string);
			
			// Adds a word to the dictionary with std::vector.push_back() and returns its position,
			// namely std::vector.size()-1.
			uint64_t AddWord(std::string);
			// Gets a word in the dictionary based on a previously known position.
			inline std::string GetWord(uint64_t);
			// Loads a dictionary into memory of the same format as /usr/share/dict/words.
			void Load(std::string);
			// Saves a dictionary to disk in the same format as /usr/share/dict/words--words delimited by
			// \n.
			void Save(std::string);
			// Just checks if we have a word using std::binary_search. Not really to be called outside
			// of the FindWord(string) function, but included as a public function for convenience.
			inline bool HasWord(std::string);
			// Finds a word in the vector using std::find and std::binary_search and returns
			// its position as an unsigned integer. This should be pretty fast, std::find is linear time
			// and std::binary_search is logarithmic.
			int64_t FindWord(std::string);
			// Sort all the words in the dictionary (presumably alphabetically).
			inline void Sort(void);
			// Grab a pointer to the word vector, if you don't want to use GetWord(uint32_t). This has to be
			// free()'ed after, and is being passed as a pointer because it's probably quite a large vector.
			std::vector<std::string>* Entries(void) { return &this->words; }
			// Tells if two dictionaries are dissimilar.
			static bool Dissimilar(Dictionary*,Dictionary*);
			// Makes a dictionary from a std::string.
			static Dictionary MakeWords(std::string);
			// Initializes a list
			void InitializeList(std::string);
			// Makes an output string from a Dictionary*.
			static std::string MakeOutput(Dictionary*);
			// Checks if two words sound alike
			bool SoundsLike(std::string,std::string);
			// Gets all the words that sound like a particular word.
			Dictionary SelectWordsThatSoundLike(std::string);
			// Get all the words between two lengths, inclusive or not or all the words of a certain length
			Dictionary SelectWordsByLength(uint64_t, uint64_t, bool);
			Dictionary SelectWordsByLength(uint64_t);
			// Get all the words with only a certain subset of characters included
			Dictionary SelectWordsWithLetters(std::string);
			// Select the words based on their first character
			Dictionary SelectWordsByFirstLetter(char);
			// Populate the soundex map
			void PopulateSoundexMap(void);
			// Get the size of the dictionary
			inline uint64_t NumberOfWords(void) {
				return this->words.size();
			}
			// Cast operator to std::vector<std::string>
			operator std::vector<std::string>() {
				return words;
			}
	};
}
#endif
